<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>iframe页面切换通知解决方案</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            padding: 20px;
            color: #333;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }

        header {
            background: #4a6fa5;
            color: white;
            padding: 20px;
            text-align: center;
        }

        h1 {
            font-size: 28px;
            margin-bottom: 10px;
        }

        .description {
            font-size: 16px;
            opacity: 0.9;
            max-width: 800px;
            margin: 0 auto;
            line-height: 1.6;
        }

        .content {
            display: flex;
            flex-wrap: wrap;
            padding: 20px;
            gap: 20px;
        }

        .control-panel {
            flex: 1;
            min-width: 300px;
            background: #f8f9fa;
            padding: 20px;
            border-radius: 8px;
            border: 1px solid #e9ecef;
        }

        .iframe-container {
            flex: 2;
            min-width: 500px;
            background: #f8f9fa;
            border-radius: 8px;
            border: 1px solid #e9ecef;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        .iframe-header {
            background: #4a6fa5;
            color: white;
            padding: 10px 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        iframe {
            width: 100%;
            flex: 1;
            border: none;
            background: white;
        }

        .url-input {
            width: 100%;
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 6px;
            margin-bottom: 15px;
            font-size: 16px;
        }

        .btn {
            background: #4a6fa5;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 16px;
            transition: background 0.3s;
            margin-right: 10px;
            margin-bottom: 10px;
        }

        .btn:hover {
            background: #3a5a80;
        }

        .btn-success {
            background: #28a745;
        }

        .btn-success:hover {
            background: #218838;
        }

        .btn-danger {
            background: #dc3545;
        }

        .btn-danger:hover {
            background: #c82333;
        }

        .preset-urls {
            margin-top: 20px;
        }

        .preset-urls h3 {
            margin-bottom: 10px;
            color: #4a6fa5;
            border-bottom: 1px solid #e9ecef;
            padding-bottom: 5px;
        }

        .status {
            margin-top: 20px;
            padding: 15px;
            background: #e7f3ff;
            border-radius: 6px;
            border-left: 4px solid #4a6fa5;
        }

        .status h3 {
            color: #4a6fa5;
            margin-bottom: 10px;
        }

        .notification-log {
            margin-top: 15px;
            max-height: 200px;
            overflow-y: auto;
            background: #f8f9fa;
            border-radius: 6px;
            padding: 10px;
            border: 1px solid #e9ecef;
        }

        .log-entry {
            padding: 8px;
            border-bottom: 1px solid #e9ecef;
            font-family: monospace;
            font-size: 14px;
        }

        .log-entry:last-child {
            border-bottom: none;
        }

        .log-time {
            color: #6c757d;
            font-size: 12px;
        }

        .code-block {
            background: #2d2d2d;
            color: #f8f8f2;
            padding: 15px;
            border-radius: 6px;
            margin-top: 10px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            font-size: 14px;
        }

        .explanation {
            margin-top: 30px;
            padding: 20px;
            background: #f8f9fa;
            border-radius: 8px;
        }

        .explanation h2 {
            color: #4a6fa5;
            margin-bottom: 15px;
            border-bottom: 1px solid #e9ecef;
            padding-bottom: 10px;
        }

        .explanation p {
            line-height: 1.6;
            margin-bottom: 15px;
        }

        .explanation ul {
            margin-left: 20px;
            margin-bottom: 15px;
        }

        .explanation li {
            margin-bottom: 8px;
            line-height: 1.5;
        }

        footer {
            text-align: center;
            padding: 20px;
            color: #6c757d;
            font-size: 14px;
            border-top: 1px solid #e9ecef;
            margin-top: 20px;
        }

        @media (max-width: 768px) {
            .content {
                flex-direction: column;
            }

            .control-panel, .iframe-container {
                min-width: 100%;
            }
        }

        .method-tabs {
            display: flex;
            margin-bottom: 15px;
            border-bottom: 1px solid #e9ecef;
        }

        .method-tab {
            padding: 10px 15px;
            cursor: pointer;
            border: 1px solid transparent;
            border-bottom: none;
            border-radius: 6px 6px 0 0;
            margin-right: 5px;
        }

        .method-tab.active {
            background: #4a6fa5;
            color: white;
        }

        .method-content {
            display: none;
        }

        .method-content.active {
            display: block;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>iframe页面切换通知解决方案</h1>
            <p class="description">演示iframe内部页面切换时如何通知父页面，并保存状态</p>
        </header>

        <div class="content">
            <div class="control-panel">
                <h2>控制面板</h2>
                <input type="text" id="urlInput" class="url-input" placeholder="输入iframe要加载的URL">
                <div>
                    <button id="loadBtn" class="btn">加载URL</button>
                    <button id="clearBtn" class="btn btn-danger">清除状态</button>
                </div>

                <div class="preset-urls">
                    <h3>测试URLs</h3>
                    <button class="btn btn-success preset-url" data-url="https://httpbin.org/html">HTTPBin HTML示例</button>
                    <button class="btn btn-success preset-url" data-url="https://httpbin.org/forms/post">HTTPBin 表单示例</button>
                    <button class="btn btn-success preset-url" data-url="https://httpbin.org/links/10/0">HTTPBin 链接页面</button>
                </div>

                <div class="status">
                    <h3>当前状态</h3>
                    <p>当前iframe URL: <span id="currentUrl">无</span></p>
                    <p>最后页面切换时间: <span id="lastChangeTime">无</span></p>

                    <div class="notification-log">
                        <h4>通知日志</h4>
                        <div id="logContainer"></div>
                    </div>
                </div>
            </div>

            <div class="iframe-container">
                <div class="iframe-header">
                    <span>iframe内容</span>
                    <span id="iframeStatus">已加载初始页面</span>
                </div>
                <iframe id="myIframe" src="about:blank"></iframe>
            </div>
        </div>

        <div class="explanation">
            <h2>iframe页面切换通知机制</h2>

            <div class="method-tabs">
                <div class="method-tab active" data-tab="method1">方法1: 轮询检测</div>
                <div class="method-tab" data-tab="method2">方法2: PostMessage通信</div>
                <div class="method-tab" data-tab="method3">方法3:  MutationObserver</div>
            </div>

            <div class="method-content active" id="method1">
                <h3>方法1: 轮询检测URL变化</h3>
                <p>通过定时器定期检查iframe的URL是否发生变化。</p>
                <div class="code-block">
// 轮询检测iframe URL变化
let lastIframeUrl = '';

function startUrlPolling() {
    setInterval(() => {
        try {
            const currentUrl = iframe.contentWindow.location.href;
            if (currentUrl !== lastIframeUrl) {
                lastIframeUrl = currentUrl;
                onIframeUrlChange(currentUrl);
            }
        } catch (e) {
            // 跨域情况下会抛出异常
            console.log('无法访问iframe URL（跨域限制）');
        }
    }, 1000); // 每秒检查一次
}
                </div>
                <p><strong>优点:</strong> 实现简单，不需要iframe配合</p>
                <p><strong>缺点:</strong> 跨域情况下无法使用，性能开销较大</p>
            </div>

            <div class="method-content" id="method2">
                <h3>方法2: PostMessage通信</h3>
                <p>iframe内部页面通过postMessage主动通知父页面。</p>
                <div class="code-block">
// 父页面监听消息
window.addEventListener('message', function(event) {
    // 验证消息来源
    if (event.origin !== 'https://expected-domain.com') return;

    if (event.data.type === 'URL_CHANGE') {
        onIframeUrlChange(event.data.url);
    }
});

// iframe内部代码（需要控制权）
window.addEventListener('popstate', function() {
    window.parent.postMessage({
        type: 'URL_CHANGE',
        url: window.location.href
    }, 'https://parent-domain.com');
});
                </div>
                <p><strong>优点:</strong> 实时性好，跨域可用（需配置）</p>
                <p><strong>缺点:</strong> 需要控制iframe内部代码</p>
            </div>

            <div class="method-content" id="method3">
                <h3>方法3: MutationObserver监听</h3>
                <p>监听iframe内容变化来推断页面切换。</p>
                <div class="code-block">
// 监听iframe内容变化
function observeIframeChanges() {
    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList') {
                // iframe内容发生变化，可能是页面切换
                onIframeContentChange();
            }
        });
    });

    observer.observe(iframe.contentDocument, {
        childList: true,
        subtree: true
    });
}
                </div>
                <p><strong>优点:</strong> 可以检测到页面内容变化</p>
                <p><strong>缺点:</strong> 跨域情况下无法使用，可能产生误报</p>
            </div>

            <h3>本示例实现方案</h3>
            <p>本示例结合了多种方法：</p>
            <ul>
                <li>对于同域iframe，使用轮询检测URL变化</li>
                <li>对于跨域iframe，使用load事件和尝试性的URL访问</li>
                <li>所有变化都通过localStorage进行状态持久化</li>
            </ul>
        </div>

        <footer>
            <p>iframe页面切换通知示例 &copy; 2023 | 尝试在iframe内点击链接，然后刷新页面查看状态保持效果</p>
        </footer>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const iframe = document.getElementById('myIframe');
            const urlInput = document.getElementById('urlInput');
            const loadBtn = document.getElementById('loadBtn');
            const clearBtn = document.getElementById('clearBtn');
            const currentUrlSpan = document.getElementById('currentUrl');
            const iframeStatus = document.getElementById('iframeStatus');
            const lastChangeTimeSpan = document.getElementById('lastChangeTime');
            const logContainer = document.getElementById('logContainer');
            const presetUrls = document.querySelectorAll('.preset-url');
            const methodTabs = document.querySelectorAll('.method-tab');

            let lastKnownUrl = '';
            let urlCheckInterval;

            // 尝试从localStorage加载保存的iframe URL
            const savedUrl = localStorage.getItem('iframeUrl');
            if (savedUrl) {
                iframe.src = savedUrl;
                currentUrlSpan.textContent = savedUrl;
                lastKnownUrl = savedUrl;
                iframeStatus.textContent = '已恢复之前的状态';
                addLog('从localStorage恢复URL: ' + savedUrl);
            }

            // 加载URL按钮事件
            loadBtn.addEventListener('click', function() {
                const url = urlInput.value.trim();
                if (url) {
                    loadUrlIntoIframe(url);
                } else {
                    alert('请输入有效的URL');
                }
            });

            // 预设URL按钮事件
            presetUrls.forEach(button => {
                button.addEventListener('click', function() {
                    const url = this.getAttribute('data-url');
                    urlInput.value = url;
                    loadUrlIntoIframe(url);
                });
            });

            // 清除状态按钮事件
            clearBtn.addEventListener('click', function() {
                localStorage.removeItem('iframeUrl');
                localStorage.removeItem('iframeLastChange');
                iframe.src = 'about:blank';
                currentUrlSpan.textContent = '无';
                lastChangeTimeSpan.textContent = '无';
                iframeStatus.textContent = '状态已清除';
                urlInput.value = '';
                lastKnownUrl = '';
                addLog('已清除所有状态');

                // 停止URL检测
                if (urlCheckInterval) {
                    clearInterval(urlCheckInterval);
                }
            });

            // iframe加载完成事件
            iframe.addEventListener('load', function() {
                iframeStatus.textContent = '加载完成';
                addLog('iframe加载完成');

                // 尝试获取当前URL（可能因跨域限制失败）
                try {
                    const currentUrl = iframe.contentWindow.location.href;
                    updateIframeUrl(currentUrl);
                    startUrlMonitoring();
                } catch (e) {
                    // 跨域情况，只能保存初始设置的URL
                    if (iframe.src !== 'about:blank') {
                        updateIframeUrl(iframe.src);
                    }
                    addLog('跨域限制，无法直接监测URL变化');
                }
            });

            // 加载URL到iframe的函数
            function loadUrlIntoIframe(url) {
                if (!url.startsWith('http://') && !url.startsWith('https://')) {
                    url = 'https://' + url;
                }

                iframeStatus.textContent = '加载中...';
                iframe.src = url;
                addLog('加载URL: ' + url);
            }

            // 更新iframe URL并保存状态
            function updateIframeUrl(url) {
                if (url !== lastKnownUrl) {
                    lastKnownUrl = url;
                    currentUrlSpan.textContent = url;

                    // 保存到localStorage
                    localStorage.setItem('iframeUrl', url);
                    localStorage.setItem('iframeLastChange', new Date().toLocaleString());
                    lastChangeTimeSpan.textContent = new Date().toLocaleString();

                    addLog('URL更新: ' + url);
                }
            }

            // 开始监控URL变化
            function startUrlMonitoring() {
                // 清除之前的监控
                if (urlCheckInterval) {
                    clearInterval(urlCheckInterval);
                }

                // 设置定时器检查URL变化
                urlCheckInterval = setInterval(function() {
                    try {
                        const currentUrl = iframe.contentWindow.location.href;
                        updateIframeUrl(currentUrl);
                    } catch (e) {
                        // 跨域情况下无法访问URL，忽略错误
                    }
                }, 1000);
            }

            // 添加日志条目
            function addLog(message) {
                const logEntry = document.createElement('div');
                logEntry.className = 'log-entry';
                logEntry.innerHTML = `<span class="log-time">${new Date().toLocaleTimeString()}</span> ${message}`;
                logContainer.appendChild(logEntry);

                // 自动滚动到底部
                logContainer.scrollTop = logContainer.scrollHeight;
            }

            // 允许通过按Enter键加载URL
            urlInput.addEventListener('keypress', function(e) {
                if (e.key === 'Enter') {
                    loadBtn.click();
                }
            });

            // 方法标签切换
            methodTabs.forEach(tab => {
                tab.addEventListener('click', function() {
                    const tabId = this.getAttribute('data-tab');

                    // 更新激活的标签
                    methodTabs.forEach(t => t.classList.remove('active'));
                    this.classList.add('active');

                    // 显示对应的内容
                    document.querySelectorAll('.method-content').forEach(content => {
                        content.classList.remove('active');
                    });
                    document.getElementById(tabId).classList.add('active');
                });
            });

            // 初始化日志
            addLog('系统初始化完成');
            if (savedUrl) {
                addLog('检测到保存的URL状态，已恢复');
            }
        });
    </script>
</body>
</html>
